//
//  AirPlayGameViewController.m
//  飞机大战
//
//  Created by sfk-ios on 2017/12/1.
//  Copyright © 2017年 sfk-JasonSu. All rights reserved.
//

#import "AirPlayGameViewController.h"
#import <QuartzCore/QuartzCore.h>

#import "ImageResources.h"
#import "SoundTool.h"
#import "GameModel.h"

#import "BackgroundView.h"
#import "HeroView.h"
#import "BulletView.h"
#import "EnemyView.h"

static long steps;

@interface AirPlayGameViewController ()

// 游戏时钟
@property (strong, nonatomic) CADisplayLink *gameTimer;
 
// 图像资源缓存
@property (strong, nonatomic) ImageResources *imagesRes;
// 声音工具
@property (strong, nonatomic) SoundTool *soundTool;

// 游戏模型
@property (strong, nonatomic) GameModel *gameModel;

// 游戏视图
@property (weak, nonatomic) UIView *gameView;

// 背景视图
@property (weak, nonatomic) BackgroundView *bgView;
// 英雄战机视图
@property (weak, nonatomic) HeroView *heroView;
// 子弹视图集合，记录屏幕中所有的子弹视图
@property (strong, nonatomic) NSMutableSet *bulletViewSet;

// 敌机集合，记录屏幕中所有飞机的视图
@property (strong, nonatomic) NSMutableSet *enemyViewSet;

// 得分标签
@property (weak, nonatomic) UILabel *scoreLabel;

/// 暂停按钮
@property (weak, nonatomic) UIButton *pauseButton;

@end

@implementation AirPlayGameViewController

- (void)viewDidLoad
{
    [super viewDidLoad];
    
    // 播放背景音乐
    [self.soundTool playMusic];
    
    steps = 0;
    
    // 实例化集合
    self.bulletViewSet = [NSMutableSet set];
    self.enemyViewSet = [NSMutableSet set];
    
	// 此方法执行时，资源已经完成加载！
    // 1) 实例化游戏模型
    CGSize heroSize = [self.imagesRes.heroFlyImages[0] size];
    self.gameModel = [GameModel gameModelWithArea:self.view.bounds
                                         heroSize:heroSize];
    [self.gameModel.hero setBullteNormalSize:self.imagesRes.bullteNormalImage.size];
    [self.gameModel.hero setBullteEnhancedSize:self.imagesRes.bullteEnhancedImage.size];
    
    // 实例化游戏视图，游戏中的所有元素，均添加到游戏视图中
    UIView *gameView = [[UIView alloc]initWithFrame:self.gameModel.gameArea];
    [self.view addSubview:gameView];
    self.gameView = gameView;
    
    // 添加暂停按钮和得分标签
    // 1> 暂停按钮
    UIButton *pauseButton = [UIButton buttonWithType:UIButtonTypeCustom];
    // 1) 设置图像
    UIImage *image = [UIImage imageNamed:@"images.bundle/BurstAircraftPause.png"];
    [pauseButton setImage:image forState:UIControlStateNormal];
    // 2) 设置高亮图像
    UIImage *imageHL = [UIImage imageNamed:@"images.bundle/BurstAircraftPauseHL.png"];
    [pauseButton setImage:imageHL forState:UIControlStateHighlighted];
    // 3) 设置按钮大小
    [pauseButton setFrame:CGRectMake(20, 20, image.size.width, image.size.height)];
    // 4) 将按钮添加到视图
    [self.view addSubview:pauseButton];
    // 5) 添加按钮监听方法
    [pauseButton addTarget:self action:@selector(tapPauseButton:) forControlEvents:UIControlEventTouchUpInside];
    self.pauseButton = pauseButton;
    
    // 2> 得分标签
    CGFloat labelX = pauseButton.frame.origin.x + pauseButton.frame.size.width;
    CGFloat labelY = 20;
    CGFloat labelW = self.gameModel.gameArea.size.width - labelX;
    CGFloat labelH = pauseButton.frame.size.height;
    
    UILabel *label = [[UILabel alloc]initWithFrame:CGRectMake(labelX, labelY, labelW, labelH)];
    [self.view addSubview:label];
    self.scoreLabel = label;
    
    // 清空背景颜色
    [label setBackgroundColor:[UIColor clearColor]];
    // 设置按钮字体
    [label setFont:[UIFont fontWithName:@"Marker Felt" size:20]];
    [label setTextColor:[UIColor darkGrayColor]];
    
    // 2) 实例化背景视图
    BackgroundView *bgView = [[BackgroundView alloc]initWithFrame:self.gameModel.gameArea image:self.imagesRes.bgImage];
    [self.gameView addSubview:bgView];
    self.bgView = bgView;
    
    // 3) 实例化英雄的视图
    HeroView *heroView = [[HeroView alloc]initWithImages:self.imagesRes.heroFlyImages];
    [heroView setCenter:self.gameModel.hero.positon];
    [self.gameView addSubview:heroView];
    
    self.heroView = heroView;
    
    // 实例化游戏时钟
    [self startGameTimer];
    
    // 监听进入后台
    [[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(DidEnterBackground) name:@"DidEnterBackground" object:nil];
}

- (void)dealloc
{
    [[NSNotificationCenter defaultCenter] removeObserver:self];
}

#pragma mark 按钮监听方法
- (void)tapPauseButton:(UIButton *)button
{
    // tag默认是0
    // 第一次点的时候，游戏是在进行时
    button.tag = !button.tag;
    
    // 如果游戏正在进行，将按钮的图像切换至开始，停止游戏时钟
    UIImage *image = nil;
    UIImage *imageHL = nil;
    
    if (button.tag) {
        image = [UIImage imageNamed:@"images.bundle/BurstAircraftStart.png"];
        imageHL = [UIImage imageNamed:@"images.bundle/BurstAircraftStartHL.png"];
        
        [self stopGameTimer];
    } else {
        // 如果游戏处于暂停状态，将按钮的图像切换至暂停，开始游戏时钟
        image = [UIImage imageNamed:@"images.bundle/BurstAircraftPause.png"];
        imageHL = [UIImage imageNamed:@"images.bundle/BurstAircraftPauseHL.png"];
        
        [self startGameTimer];
        [self.soundTool playMusic];
    }
    
    [button setImage:image forState:UIControlStateNormal];
    [button setImage:imageHL forState:UIControlStateHighlighted];
}

#pragma mark - 触摸事件
#pragma mark 触摸移动
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
    // 1. 取出触摸对象
    UITouch *touch = [touches anyObject];
    
    // 注意：因为用户只能移动英雄，因此将触摸监控范围设置在整个屏幕上
    // 2. 取出当前触摸点
    CGPoint location = [touch locationInView:self.gameView];
    // 3. 取出之前触摸点
    CGPoint preLocation = [touch previousLocationInView:self.gameView];
    
    // 4. 计算偏移量
    CGPoint offset = CGPointMake(location.x - preLocation.x, location.y - preLocation.y);
    
    // 5. 更新英雄的位置
    CGPoint position = self.gameModel.hero.positon;
    self.gameModel.hero.positon = CGPointMake(position.x + offset.x, position.y + offset.y);
}


#pragma mark - 私有方法
#pragma mark 时钟触发方法
- (void)step
{
    steps++;
    
    // 让游戏模型向下移动背景位置
    [self.gameModel bgMoveDown];
    // 更新背景图片位置
    [self.bgView changBGWithFrame1:self.gameModel.bgFrame1 rame2:self.gameModel.bgFrame2];
    
    // 更新英雄位置
    [self.heroView setCenter:self.gameModel.hero.positon];
    
    // 发射子弹
    // 每秒发射三次(需要时钟步长)
    if (steps % 20 == 0) {
        // 仅产生子弹的数据，并没有添加图片到界面
        [self.gameModel.hero fire];
        
        [self.soundTool playSoundByFileName:@"bullet"];
    }
    
    // 每次时钟触发，都需要检查更新屏幕上所有子弹的位置
    [self checkBullets];
    
    // 创建敌机
    // 在时钟方法中每秒创建3架小飞机，每隔10秒随机创建中飞机或者大飞机
    if (steps % 20 == 0) {
        // 1）创建模型
        Enemy *enemy = nil;
        
        if (steps % (5 * 60) == 0) {
            // 随机出现大飞机或者中飞机，先随机出来飞机的类型
            EnemyType type = (arc4random_uniform(2) == 0) ? kEnemyMiddle : kEnemyBig;
            
            CGSize size = self.imagesRes.enemyMiddleImage.size;
            if (kEnemyBig == type) {
                size = [self.imagesRes.enemyBigImages[0]size];
            }
            enemy = [self.gameModel createEnemyWithType:type size:size];
        } else {
            // 小飞机
            enemy = [self.gameModel createEnemyWithType:kEnemySmall size:self.imagesRes.enemySmallImage.size];
        }
        
        // 2）根据模型创建飞机视图
        EnemyView *enemyView = [[EnemyView alloc]initWithEnemy:enemy imageRes:self.imagesRes];
        
        // 3) 将敌机视图添加到集合及视图
        [self.enemyViewSet addObject:enemyView];
        [self.gameView addSubview:enemyView];
    }
    
    // 调用一个方法，更新飞机位置
    [self updateEnemys];
    
    // 碰撞检测
    [self collisionDetector];
}

#pragma mark 更新游戏得分标签
- (void)updateScoreLabel
{
    // 判断游戏模型中的分数，如果==0，清空标签内容
    if (self.gameModel.score == 0) {
        [self.scoreLabel setText:@""];
    } else {
        NSString *str = [NSString stringWithFormat:@"%ld000", self.gameModel.score];
        
        [self.scoreLabel setText:str];
    }
}

#pragma mark 碰撞检测
- (void)collisionDetector
{
    // 爆炸动画播放太快，可以使用steps来调整爆炸动画
    if (steps % 10 == 0) {
        // 遍历飞机集合，判断toBlowup为真的飞机开始播放动画
        NSMutableSet *toRemovedSet = [NSMutableSet set];
        for (EnemyView *enemyView in self.enemyViewSet) {
            Enemy *enemy = enemyView.enemy;
            
            if (enemy.toBlowup) {
                [enemyView setImage:enemyView.blowupImages[enemy.blowupFrames++]];
            }
            
            // 判断动画是否播放到最后一帧
            if (enemy.blowupFrames == enemyView.blowupImages.count) {
                // 需要从集合中删除飞机
                [toRemovedSet addObject:enemyView];
            }
        }
        
        for (EnemyView *enemyView in toRemovedSet) {
            // 1. 修改游戏模型中的得分
            self.gameModel.score += enemyView.enemy.score;
            // 2. 更新得分标签的显示
            [self updateScoreLabel];
            
            [self.enemyViewSet removeObject:enemyView];
            [enemyView removeFromSuperview];
        }
        [toRemovedSet removeAllObjects];
    }
    
    // 资源：子弹的集合，敌机的集合
    for (BulletView *bulletView in self.bulletViewSet) {
        Bullet *bullet = bulletView.bullet;
        
        for (EnemyView *enemyView in self.enemyViewSet) {
            Enemy *enemy = enemyView.enemy;
            
            // 检查子弹和敌机的frame是否相交
            // 如果敌机处于爆炸中，不做子弹和敌机的碰撞检测
            if (CGRectIntersectsRect(bulletView.frame, enemyView.frame) && !enemy.toBlowup) {
                // 用子弹的damage减敌机的hp
                enemy.hp -= bullet.damage;
                
                // 如果敌机的生命值<=0，需要销毁
                if (enemy.hp <= 0) {
                    NSLog(@"to blow up");
                    enemy.toBlowup = YES;
                    // 需要播放飞机爆炸的序列帧图片
                } else {
                    // 如果>0，显示挨揍图片
                    // 因为大飞机播放序列帧动画，如果仅仅改变image，而不停止序列帧动画，用户看不到图像的变化
                    // 如果是大飞机，需要停止序列帧动画
                    if (enemy.type == kEnemyBig) {
                        [enemyView stopAnimating];
                    }
                    enemyView.image = enemyView.hitImage;
                }
            }
        }
    }
    
    // 英雄和敌机的碰撞
    // 遍历敌机集合，如果和英雄发生碰撞，英雄OVER，游戏结束
    for (EnemyView *enemyView in self.enemyViewSet) {
        if (CGRectIntersectsRect(enemyView.frame, self.gameModel.hero.collisionFrame)) {
            NSLog(@"英雄牺牲了");
            [self.soundTool playSoundByFileName:@"game_over"];
            
            // 英雄爆炸的镜头，因为英雄挂掉，游戏就结束了
            // 不再需要时钟方法，因此此处可以用序列帧实现
            // 1) 停止当前英雄的序列帧动画
            [self.heroView stopAnimating];
            // 设置英雄爆炸后，最后的影像
            [self.heroView setImage:self.imagesRes.heroBlowupImages[3]];
    
            // 2) 更换英雄的序列帧图片
            [self.heroView setAnimationImages:self.imagesRes.heroBlowupImages];
            // 3) 设置序列帧时长
            [self.heroView setAnimationDuration:1.0f];
            // 动画重复次数
            [self.heroView setAnimationRepeatCount:1];
            // 4)　启动序列帧动画
            [self.heroView startAnimating];
            // 5) 播放一次后，关闭时钟
            [self performSelector:@selector(stopGameTimer) withObject:nil afterDelay:1.0f];
            
            // 死了
            [self showDeathMessage];
            // 直接跳出循环，不再继续遍历
            break;
        }
    }
}

/// 显示死亡信息
- (void)showDeathMessage
{
    // 死亡之后的操作
    NSString *msg = [NSString stringWithFormat:@"游戏结束，最终得分：%ld000", self.gameModel.score];
    UIAlertController *alertVc = [UIAlertController alertControllerWithTitle:@"Game Over" message:msg preferredStyle:UIAlertControllerStyleAlert];
    
    // 1.继续游戏
    UIAlertAction *onceMore = [UIAlertAction actionWithTitle:@"继续" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
        
        // 重新设置英雄战机
        [self.heroView removeFromSuperview];
        HeroView *heroView = [[HeroView alloc]initWithImages:self.imagesRes.heroFlyImages];
        CGRect gameArea = self.gameModel.gameArea;
        self.gameModel.hero.positon = CGPointMake(gameArea.size.width *0.5, gameArea.size.height -heroView.frame.size.height);
        [heroView setCenter:self.gameModel.hero.positon];
        [self.gameView addSubview:heroView];
        self.heroView = heroView;
        
        // 开始游戏时钟
        [self startGameTimer];
    }];
    
    // 2.退出游戏
    UIAlertAction *quit = [UIAlertAction actionWithTitle:@"不玩了" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
        
        CGSize screenSize  = [UIScreen mainScreen].bounds.size;
        UILabel *goodbyeL = [[UILabel alloc]initWithFrame:CGRectMake(0, 0, 1, 1)];
        goodbyeL.center = self.view.window.center;
        goodbyeL.font = [UIFont boldSystemFontOfSize:50];
        goodbyeL.textColor = [UIColor lightGrayColor];
        goodbyeL.text = @"ค(TㅅT)再见";
        [self.view.window addSubview:goodbyeL];
        
        // 动画过渡退出
        [UIView animateWithDuration:2.0 animations:^{
            
            self.view.alpha = 0.2;
            goodbyeL.frame = CGRectMake(100, 0, screenSize.width, screenSize.width);
            goodbyeL.center = self.view.window.center;
            
        } completion:^(BOOL finished) {
            [UIView animateWithDuration:1.0 animations:^{
                
                self.view.transform = CGAffineTransformMakeScale(0.2, 0.1);
                
            } completion:^(BOOL finished) {
                
                exit(0);
            }];
        }];
        
    }];
    [alertVc addAction:onceMore];
    [alertVc addAction:quit];
    [self presentViewController:alertVc animated:YES completion:nil];
}

#pragma mark 开始游戏时钟
- (void)startGameTimer
{
    // 1) 实例化游戏时钟
    self.gameTimer = [CADisplayLink displayLinkWithTarget:self selector:@selector(step)];
    // 2) 添加到主运行循环
    [self.gameTimer addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
}

#pragma mark 停止游戏时钟
- (void)stopGameTimer
{
    // 通过时钟来处理界面的变化，如果把时钟停止，游戏将停止
    [self.gameTimer invalidate];
}


#pragma mark 更新飞机位置
- (void)updateEnemys
{
    NSLog(@"敌机数量 %ld", self.enemyViewSet.count);

    // 每一帧按照集合中(遍历)飞机的速度（speed）下降
    // 问题：英雄太容易牺牲了
    for (EnemyView *enemyView in self.enemyViewSet) {
        Enemy *enemy = enemyView.enemy;
        
        // 1) 更新位置position属性
        enemy.position = CGPointMake(enemy.position.x, enemy.position.y + enemy.speed);
        // 2) 根据position属性，调整敌机位置
        [enemyView setCenter:enemy.position];
    }
    
    // 遍历集合，如果飞机移除屏幕，需要从集合和视图中删除
    // 与子弹类似，遍历集合时不能直接删除
    NSMutableSet *toRemovedSet = [NSMutableSet set];
    
    for (EnemyView *enemyView in self.enemyViewSet) {
        if (enemyView.frame.origin.y >= self.gameModel.gameArea.size.height) {
            // 需要删除
            [toRemovedSet addObject:enemyView];
        }
    }
    
    // 遍历要删除的集合，清除内容
    for (EnemyView *enemyView in toRemovedSet) {
        [self.enemyViewSet removeObject:enemyView];
        [enemyView removeFromSuperview];
    }
    // 清空临时集合
    [toRemovedSet removeAllObjects];
}

#pragma mark 检查子弹
- (void)checkBullets
{
    NSLog(@"子弹数量 %ld", self.bulletViewSet.count);

    // 因为此方法调用频繁，不适宜使用懒加载的方式
//    if (self.bulletViewSet == nil) {
//        self.bulletViewSet = [NSMutableSet set];
//    }
    // 如果要删除集合中的数据，需要使用一个缓冲集合
    NSMutableSet *toRemovedSet = [NSMutableSet set];
    
    // 每次向上移动5个点子弹
    for (BulletView *bulletView in self.bulletViewSet) {
        // 计算子弹新位置，向上移动的数值越大，对英雄越不利！
        CGPoint position = CGPointMake(bulletView.center.x, bulletView.center.y - 8.0);
        // 移动子弹视图
        [bulletView setCenter:position];
        
        // 判断子弹是否飞出屏幕
        if (CGRectGetMaxY(bulletView.frame) < 0) {
            [toRemovedSet addObject:bulletView];
        }
    }
    
    // 遍历要删除的集合，清除飞出屏幕的子弹
    for (BulletView *bulletView in toRemovedSet) {
        // 从视图中删除
        [bulletView removeFromSuperview];
        // 从集合中删除
        [self.bulletViewSet removeObject:bulletView];
    }
    // 清空缓存集合
    [toRemovedSet removeAllObjects];
    
    // 根据model.hero中新增加的子弹数据，添加子弹视图
    for (Bullet *bullet in self.gameModel.hero.bullteSet) {
        // 新建子弹视图
        UIImage *image = self.imagesRes.bullteNormalImage;
        if (bullet.isEnhanced) {
            image = self.imagesRes.bullteEnhancedImage;
        }

        // 新建子弹视图
        BulletView *bulletView = [[BulletView alloc]initWithImage:image bullet:bullet];
        // 设置子弹中心点
        [bulletView setCenter:bullet.position];
        // 将子弹添加到视图
        [self.gameView addSubview:bulletView];
        // 将子弹添加到子弹视图集合中
        [self.bulletViewSet addObject:bulletView];
    }
    // 清空英雄中的子弹集合
    [self.gameModel.hero.bullteSet removeAllObjects];
}

#pragma mark - 成员方法
#pragma mark 加载图像资源
- (void)loadResources
{
    // 加载图像缓存
    self.imagesRes = [[ImageResources alloc]init];
    // 加载音乐及音效文件
    self.soundTool = [[SoundTool alloc]init];
}

#pragma mark - 进入后台
- (void)DidEnterBackground
{
    if (!self.pauseButton.tag) {
        [self tapPauseButton:self.pauseButton];
        [self.soundTool pauseMusic];
    }
}

@end
